namespace FIREFLYX {

    /** uniform 用户输入属性值类型 */
    export type UniformUserValue = BufferSource | number | Vector2 | Vector3 | Vector4 | Color3 | Color4 | Rect | Matrix | TextureBase | null;
    /** uniform 标准化属性值类型 */
    export type UnifromNormalValue = BufferSource | TextureBase;
    /** shader 的 uniform 标准化属性值类型 字典容器类型 */
    export type ShaderPropertyMap = { [name: string]: UnifromNormalValue | UnifromNormalValue[] };
    /**
     * shader uniform 属性对象
     */
    class ShaderProperty {
        public type: "texture" | "float" | "color" | "vector2" | "vector3" | "vector4";
        public value: string | number | number[];
    }

    /**
     * 编译后的着色器对象
     */
    export class ShaderCompiled {
        private static _errorFallBack: ShaderCompiled;
        private _program: Nullable<WebGLProgram>;
        private _unifInfoList: WebGLActiveInfo[] = [];
        private _unifLocationList: Nullable<WebGLUniformLocation>[] = [];
        private _unifBlockInfoList: { name: string, dSize: number, unifIdxs: number[] }[] = [];
        private _unifIndexIsBlockMap: { [idx: number]: boolean } = {};
        private _unifBlockNameIndexMap: { [bName: string]: number } = {};
        /** 着色器程序 编译后的 WebGLProgram 对象   */
        public get program() {
            if (!this._program) return ShaderCompiled.errorFallBack._program;
            return this._program;
        }
        /** 着色器 unifrom 信息列表 */
        public get unifInfoList() { return this._unifInfoList; }
        /** 着色器 unifrom 地址对象列表 */
        public get unifLocationList() { return this._unifLocationList; }
        /** 着色器 unifrom block 信息列表 */
        public get unifBlockInfoList() { return this._unifBlockInfoList; }
        /** 着色器 unifrom 索引 字典 {unifrom索引 : 是block}*/
        public get unifIndexIsBlockMap() { return this._unifIndexIsBlockMap; }
        /** 着色器 unifrom block 名 索引 字典 {block名 : block索引} */
        public get unifBlockNameIndexMap() { return this._unifBlockNameIndexMap; }
        /** 错误的FallBack ShaderCompiled实例 */
        public static get errorFallBack() {
            let errFB = ShaderCompiled._errorFallBack;
            if (!errFB) {
                const vsCode = `#version 300 es
                precision mediump float;
                layout (location = 0) in vec4 a_Position;   //顶点
                uniform ubo_model_block{
                    mat4 u_mMtx;                            //模型矩阵
                } uboModel;
                uniform ubo_cam_block{
                    mat4 u_viewMtx;                         //视窗矩阵
                    mat4 u_projectMtx;                      //投影矩阵
                    vec4 u_eyePos;                          //眼睛位置
                } uboCam;
                void main() {
                    mat4 mvpMat = uboCam.u_projectMtx * uboCam.u_viewMtx * uboModel.u_mMtx;
                    gl_Position = mvpMat * a_Position;
                }
                `;
                const fsCode = `#version 300 es
                precision mediump float;
                out vec4 color;
                void main(){
                    color = vec4(1.,0.,1.,1.);              //仅输出紫色
                }
                `;
                //
                errFB = new ShaderCompiled(vsCode, fsCode);
                ShaderCompiled._errorFallBack = errFB;
            }
            return errFB;
        }

        /**
         * 传入着色器代码 并立即编译
         * @param vs 顶点着色器代码
         * @param fs 片元着色器代码
         * @param keywordIterator0 关键词迭代器0 
         * （为了优化不用 ...keywordIterator 来实现）
         * @param keywordIterator1 关键词迭代器1
         * @param keywordIterator2 关键词迭代器2
         */
        constructor(vs: string, fs: string,
            keywordIterator0: Nullable<IterableIterator<string>> = null,
            keywordIterator1: Nullable<IterableIterator<string>> = null,
            keywordIterator2: Nullable<IterableIterator<string>> = null) {

            const gl = App.webgl2;
            //keywords 设置 define
            let defKWStr = ``;
            //打上变体 宏定义
            defKWStr += ShaderCompiled.Getkeyword2Str(keywordIterator0);
            defKWStr += ShaderCompiled.Getkeyword2Str(keywordIterator1);
            defKWStr += ShaderCompiled.Getkeyword2Str(keywordIterator2);

            //es 300 标记检测 , define 插入
            let idx = ShaderCompiled.GetEs300Index(vs);
            vs = `${vs.substring(0, idx)}\n${defKWStr}${vs.substring(idx)}`;
            idx = ShaderCompiled.GetEs300Index(fs);
            fs = `${fs.substring(0, idx)}\n${defKWStr}${fs.substring(idx)}`;

            //编译
            this._program = ShaderCompiled.InitProgram(gl, vs, fs);
            //初始化 着色器属性信息
            this.initPropertyInfos();
        }

        /** 初始化 属性信息 */
        private initPropertyInfos() {
            if (!this._program) return;
            const gl = App.webgl2;
            const p = this._program;
            //收集 着色器 属性
            //unifrom
            const unifNum = gl.getProgramParameter(p, gl.ACTIVE_UNIFORMS);
            const unifBlockNum = gl.getProgramParameter(p, gl.ACTIVE_UNIFORM_BLOCKS);
            const unifInfoList: WebGLActiveInfo[] = [];
            const unifLocationList: Nullable<WebGLUniformLocation>[] = [];
            for (let i = 0; i < unifNum; i++) {
                const unifInfo = gl.getActiveUniform(p, i) as WebGLActiveInfo;
                const unifLoc = gl.getUniformLocation(p, unifInfo.name);
                unifInfoList.push(unifInfo);
                unifLocationList.push(unifLoc);
            }

            const blockInfoList: { name: string, dSize: number, unifIdxs: number[] }[] = [];
            const blockMap: { [idx: number]: boolean } = {};
            const blockNameIndex: { [bName: string]: number } = {};
            for (let i = 0; i < unifBlockNum; i++) {
                const name = gl.getActiveUniformBlockName(p, i) as string;
                blockNameIndex[name] = i;
                const dSize = gl.getActiveUniformBlockParameter(p, i, gl.UNIFORM_BLOCK_DATA_SIZE) as number;
                const unifIdxs = gl.getActiveUniformBlockParameter(p, i, gl.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES) as number[];
                unifIdxs.forEach((idx) => { blockMap[idx] = true; });
                blockInfoList.push({ name, dSize, unifIdxs });
            }

            //
            this._unifInfoList = unifInfoList;
            this._unifLocationList = unifLocationList;
            this._unifBlockInfoList = blockInfoList;
            this._unifIndexIsBlockMap = blockMap;
            this._unifBlockNameIndexMap = blockNameIndex;
        }

        /**
         * 初始化着色器
         * @param gl WebGL2RenderingContext 对象
         * @param vs 顶点着色器代码
         * @param fs 片元着色器代码
         * @returns WebGLProgram 对象
         */
        public static InitProgram(gl: WebGL2RenderingContext, vs: string, fs: string): Nullable<WebGLProgram> {
            let shaderCompileFailed = false;
            let shaderLinkFailed = false;
            //顶点shader
            //创建webglShader 对象
            let vsSh = gl.createShader(gl.VERTEX_SHADER);
            if (!vsSh) {
                console.error(`createShader(VERTEX_SHADER) fail.`);
                return null;
            }

            //将字符串程序代码传入
            gl.shaderSource(vsSh, vs);
            //编译字符串程序代码成二进制对象,便于webgl使用
            gl.compileShader(vsSh);
            if (!this.CkShaderCompileS(gl, vsSh, "Vertex Shader")) {
                shaderCompileFailed = true;
            }

            //片源shader
            let fsSh = gl.createShader(gl.FRAGMENT_SHADER);
            if (!fsSh) {
                console.error(`createShader(FRAGMENT_SHADER) fail.`);
                return null;
            }
            gl.shaderSource(fsSh, fs);
            gl.compileShader(fsSh);
            if (!this.CkShaderCompileS(gl, fsSh, "Fragment Shader")) {
                shaderCompileFailed = true;
            }

            //创建WebGLProgram
            let pVs = gl.createProgram();
            if (!pVs) {
                console.error(`createProgram() fail.`);
                return null;
            }
            //附加 shader程序
            gl.attachShader(pVs, vsSh);
            gl.attachShader(pVs, fsSh);
            //如果 vsSh ， fsSh 不需要缓存，可 gl.deleteShader(vsSh);

            //shader err log
            let log = gl.getShaderInfoLog(vsSh);
            if (log) {
                console.error(`${vs} \n ${log}`);
            }

            log = gl.getShaderInfoLog(fsSh);
            if (log) {
                console.error(`${fs} \n ${log}`);
            }

            //链接到一个已经 attach的程序
            gl.linkProgram(pVs);
            // //添加指定的程序 到当前的渲染状态中去 (为什么 要 link 又要 use ?)
            // gl.useProgram(pVs);
            let r3 = gl.getProgramParameter(pVs, gl.LINK_STATUS);
            if (r3 == false) {
                // console.error("vs:" + nameVS + "   fs:" + nameFS + "a webgl program error:" + webgl.getProgramInfoLog(program));
                console.error("a webgl program error:" + gl.getProgramInfoLog(pVs));
                gl.deleteProgram(pVs);
                shaderLinkFailed = true;
                // return null;
            }

            const hasErrStr = this.CatchErrorStr(gl);
            if (hasErrStr || shaderLinkFailed || shaderCompileFailed) {
                return null;
            }

            return pVs;
        }

        /**
         * 获取 keyword 转到 宏定义 字符串
         * @param keywordIterator keyword迭代器
         * @returns 宏定义 字符串
         */
        private static Getkeyword2Str(keywordIterator: Nullable<IterableIterator<string>>): string {
            let result = ``;
            if (!keywordIterator) return result;
            let next: IteratorResult<string, any>;
            while (!(next = keywordIterator.next()).done) {
                const keywordStr = next.value;
                if (!keywordStr) continue;
                result += `#define ${keywordStr}\n`;
            }

            return result;
        }

        /**
         * 获取ES 300 标记索引
         * @param shaderCode 
         * @returns 
         */
        private static GetEs300Index(shaderCode: string) {
            const regEs300 = /#version *300 *es *[\n|\r]/;
            let idx = 0;
            let ex = regEs300.exec(shaderCode);
            if (ex && ex.length > 0) {
                idx = ex[0].length;
            }
            return idx;
        }

        /** 检查shader编译 */
        private static CkShaderCompileS(gl: WebGL2RenderingContext, sh: WebGLShader, info = ""): boolean {
            let s = gl.getShaderParameter(sh, gl.COMPILE_STATUS);
            if (s == false) {
                console.error(`compiles shader error : ${info}`);
                return false;
            }
            return true;
        }

        /** 获得shader编译错误信息 并打印 */
        private static CatchErrorStr(gl: WebGL2RenderingContext): boolean {
            let str = ``;
            let val = gl.getError();
            switch (val) {
                case gl.INVALID_ENUM: str = `An unacceptable value has been specified for an enumerated argument. `; break;
                case gl.INVALID_VALUE: str = `A numeric argument is out of range. `; break;
                case gl.INVALID_OPERATION: str = `The specified command is not allowed for the current state. `; break;
                case gl.INVALID_FRAMEBUFFER_OPERATION: str = `The currently bound framebuffer is not framebuffer complete when trying to render to or to read from it.`; break;
                case gl.OUT_OF_MEMORY: str = `Not enough memory is left to execute the command.`; break;
                case gl.CONTEXT_LOST_WEBGL: str = `If the WebGL context is lost, this error is returned on the first call to getError.
                                                   Afterwards and until the context has been restored, it returns gl.NO_ERROR.`;
                    // tslint:disable-next-line: align
                    break;
                default:
            }

            if (str != ``)
                console.error(`webgl error : ${str}`);

            return str != ``;
        }
    }

    /** 字典树节点 */
    class KeywordTrieNode {
        /** 链ID */
        private static _chainIDCount: number = 0;
        private static _ChainKeyIDMap: Map<string, number> = new Map();
        private static getChainID(chainKey: string) {
            let id = this._ChainKeyIDMap.get(chainKey);
            if (id == null) {
                id = this._chainIDCount++;
                this._ChainKeyIDMap.set(chainKey, id);
            }
            return id;
        }

        private _key: string;
        private _chainID: number;
        private _chainKey: string;
        // private _childMap: Nullable<Map<string, KeywordTrieNode>>;
        private _childMap: Nullable<Map<string, KeywordTrieNode>>;

        /** id */
        public get chainID() { return this._chainID; }
        /** 键值 */
        public get key() { return this._key; }
        /** 父节点 */
        public parent: Nullable<KeywordTrieNode>;
        /** 子节点字典容器 */
        public get childMap() {
            if (this._childMap == null) {
                this._childMap = new Map();
            }
            return this._childMap as Map<string, KeywordTrieNode>;
        }

        constructor(key: string) {
            this._key = key;
        }

        /** 刷新ChainID */
        public refreshChainID() {
            if (this.chainID == -1) return; //root节点跳过
            //从头顺一遍
            let node = this as KeywordTrieNode;

            //更新 chainKey
            // this._chainKey = keys.sort().toString();

            let chainkey = ``;
            if (node) {
                if (node.parent) {
                    chainkey = `${node.parent._chainKey}_${node.key}`;
                } else {
                    chainkey = `${node.key}`;
                }
            }

            //
            // let keys: string[] = [];
            // while (node && node.parent) {
            //     keys.push(node.key);
            //     node = node.parent;
            // }
            // chainkey = keys.toString();

            this._chainKey = chainkey;
            this._chainID = KeywordTrieNode.getChainID(this._chainKey);
        }

        public add(node: KeywordTrieNode) {
            let cMap = this.childMap;
            node.parent = this;
            cMap.set(node.key, node);
        }

        public remove(keyword: string) {
            //
        }
    }

    /** 字典树 */
    class KeywordTrieTree {
        private _chainIDNodeMap: Map<number, KeywordTrieNode> = new Map();
        private _rootNode: KeywordTrieNode = new KeywordTrieNode(``);

        // public getChainID(keywords: string[]): number {
        //     //
        //     return -1;
        // }

        /**
         * 获取一个合并指定关键字的chainID
         * @param srcChainID 被合并的chainID
         * @param keyword 合并的指定关键字
         * @returns 返回新的chainID
         */
        public chainIDMergeKeyword(srcChainID: number, keyword: string): number {
            //先找map中是否有节点
            let rootNode = this._chainIDNodeMap.get(srcChainID);
            if (!rootNode) rootNode = this._rootNode;

            let tarNode: KeywordTrieNode;
            //看一下子节点中是否有 key
            const tempNode = rootNode.childMap.get(keyword);
            if (tempNode) {
                return tempNode.chainID;
            }

            //new node
            tarNode = new KeywordTrieNode(keyword);
            rootNode.add(tarNode);
            tarNode.refreshChainID();
            const newChainID = tarNode.chainID;
            this._chainIDNodeMap.set(newChainID, tarNode);
            return newChainID;
        }

        /**
         * 获取一个拆分指定关键字的chainID
         * @param keyword 拆分的关键字
         * @param keywordMap 现有关键字容器
         * @returns 返回新的chainID
         */
        public chainIDSplitKeyword(keyword: string, keywordMap: Map<string, boolean>): number {
            //先找map中是否有节点
            let currNode = this._rootNode;
            let next;
            let mapKeys = keywordMap.keys();
            let childMap = currNode.childMap;
            const chainIDNodeMap = this._chainIDNodeMap;
            while (!(next = mapKeys.next()).done) {
                const k = next.value;
                //是指定keyword 跳过
                if (k == keyword || childMap.has(k)) continue;
                //没有的话 并建立
                let tarNode: KeywordTrieNode = childMap.get(k) as any;
                tarNode = new KeywordTrieNode(k);
                currNode.add(tarNode);
                tarNode.refreshChainID();
                chainIDNodeMap.set(tarNode.chainID, tarNode);
                currNode = tarNode;
                childMap = currNode.childMap;
            }

            return currNode.chainID;
        }
    }

    /** 着色器关键字 */
    export class ShaderKeyword {
        public static readonly DefaultChainID = -1;
        private static _keywordTrieTree: KeywordTrieTree = new KeywordTrieTree();
        private static _cacheKeywordMap: Map<number, ShaderKeyword> = new Map();

        private _chainID: number;
        public get chainID() { return this._chainID; }
        // private _keywords: string[] = [];
        private _keywordMap: Map<string, boolean> = new Map();
        private _inited = false;

        private constructor(chainID: number) {
            this._chainID = chainID;
        }

        /**
         * 通过chainID 获取 ShaderKeyword对象
         * @param chainID 
         * @returns ShaderKeyword对象
         */
        public static Get(chainID: number = ShaderKeyword.DefaultChainID): ShaderKeyword {
            if (chainID == null || isNaN(chainID)) chainID = ShaderKeyword.DefaultChainID;

            let kw = this._cacheKeywordMap.get(chainID);
            if (kw) { return kw; }

            const defKWChainID = ShaderKeyword.DefaultChainID;
            //创建一个新的keyword对象
            if (chainID != defKWChainID) {
                const newKW = new ShaderKeyword(chainID);
                this._cacheKeywordMap.set(chainID, newKW);
                return newKW;
            }

            //返回默认（-1）的keyword
            let kwDef = this._cacheKeywordMap.get(defKWChainID);
            if (!kwDef) {
                kwDef = new ShaderKeyword(defKWChainID);
                this._cacheKeywordMap.set(kwDef._chainID, kwDef);
            }
            return kwDef;
        }

        /**
         * 合并连个 ShaderKeyword 对象
         * @param a ShaderKeyword 对象 a
         * @param b ShaderKeyword 对象 b
         * @returns 合并后的 ShaderKeyword 对象
         */
        public static Merge(a: ShaderKeyword, b: ShaderKeyword) {
            const def = ShaderKeyword.Get();
            if (a == null || b == null) return def;
            if ((a != null && b == null) || a == b) return a;
            if (b != null && a == null) return b;
            let result: ShaderKeyword = a;
            const bKeys = b.getKeys();
            let next: IteratorResult<string, any>;
            while (!(next = bKeys.next()).done) {
                const val = next.value;
                if (!val) continue;
                const aKeyWorld = val;
                result = a.enable(aKeyWorld);
            }
            return result;
        }

        private initKeywordMerge(keys: Nullable<IterableIterator<string>>, addKeyword: string = "") {
            if (this._inited || !keys) return;
            let next: IteratorResult<string, any>;
            const keywordMap = this._keywordMap;
            while (!(next = keys.next()).done) {
                const val = next.value;
                if (!val) continue;
                keywordMap.set(next.value, true);
            }
            //
            if (addKeyword) this._keywordMap.set(addKeyword, true);
            this._inited = true;
        }

        private initKeywordSplit(keys: Nullable<IterableIterator<string>>, removeKeyword: string = "") {
            if (this._inited || !keys) return;
            let next: IteratorResult<string, any>;
            const keywordMap = this._keywordMap;
            while (!(next = keys.next()).done) {
                const val = next.value;
                if (val == removeKeyword) continue;
                keywordMap.set(val, true);
            }
            this._inited = true;
        }

        /**
         * 启用一个关键字
         * @param keyword 关键字
         * @returns ShaderKeyword对象（可能是自己，可能是一个新的对象）
         */
        public enable(keyword: string): ShaderKeyword {
            //当前对象已经包含 keyword 不做任何处理
            if (this._keywordMap.has(keyword)) return this;
            const oldKW = this as ShaderKeyword;
            const newChainID = ShaderKeyword._keywordTrieTree.chainIDMergeKeyword(this._chainID, keyword);
            const newKW = ShaderKeyword.Get(newChainID);
            if (!newKW._inited) {
                newKW.initKeywordMerge(oldKW.getKeys(), keyword);
            }
            return newKW;
        }

        /**
         * 取消一个关键字的启用状态
         * @param keyword 关键字
         * @returns ShaderKeyword对象（可能是自己，可能是一个新的对象）
         */
        public disable(keyword: string): ShaderKeyword {
            //当前对象不包含 keyword 不做任何处理
            if (!this._keywordMap.has(keyword)) return this;
            const oldKW = this as ShaderKeyword;
            const newChainID = ShaderKeyword._keywordTrieTree.chainIDSplitKeyword(keyword, this._keywordMap);
            const newKW = ShaderKeyword.Get(newChainID);
            if (!newKW._inited) {
                newKW.initKeywordSplit(oldKW.getKeys(), keyword);
            }
            return newKW;
        }

        /**
         * 获取 所有keyword 的迭代器
         * @returns 所有keyword 的迭代器
         */
        public getKeys() {
            return this._keywordMap.keys();
        }
    }

    /** 
     * 着色器资源
     * shader keywords 规则参考
     * https://docs.unity3d.com/cn/current/Manual/shader-keywords.html
     */
    export class Shader extends Asset {
        /** shader 识别标签 */
        public tag: string = "";
        /** 光照模式标记 */
        public lightMode: string = "";
        /** 用户定义的shader 属性字典 */
        public definePropertyMap: { [key: string]: ShaderProperty };
        // /** 多编译变体,关键字列表参数 */
        // public multiCompiles: string[][];
        /** 顶点 着色器代码文件 */
        public vertexFile: string;
        /** 片元 着色器代码文件 */
        public fragmentFile: string;
        /** 文件路径 */
        public filePath: string = "";
        /** 顶点 着色器 GLSL 对象 */
        public vertexGLSL: GLSL;
        /** 片元 着色器 GLSL 对象 */
        public fragmentGLSL: GLSL;
        private _isLoadAll: boolean = false;
        // private _variantMap: { [kwStr: string]: ShaderCompiled } = {};
        private _variantIDMap: { [variantID: string]: ShaderCompiled } = {};
        // private _currKeywordStr: string = "";
        // private _currKeywords: string[] = [];
        private _propertyMap: ShaderPropertyMap = {};//属性值容器
        /** 全局属性 字典容器 */
        private static _globalPropertyMap: ShaderPropertyMap = {};//属性值容器
        //shader 的 关键字
        private static _keyword: ShaderKeyword = ShaderKeyword.Get();

        /** shader的属性值字典 */
        public get propertyMap() { return this._propertyMap; }
        /**  shader的全局 属性值字典  */
        public static get globalPropertyMap() { return Shader._globalPropertyMap; }

        /**
         * 获取 WebGLProgram 对象
         * @param keywordStr 变体关键字
         * @returns WebGLProgram 对象
         */
        // public getProgram(keywordStr?: string) {
        //     const comp = this.getCompiled(keywordStr);
        //     if (!comp) return null;
        //     return comp.program;
        // }

        /**
         * 获取 编译了的着色器
         * @param materialKeywordID  材质的 关键字ID （不传，则忽略）
         * @param otherKeywordID  其他的 关键字ID （不传，则忽略）
         * @returns 编译了的着色器对象
         */
        public getCompiled(materialKeywordID?: number, otherKeywordID?: number) {
            const defID = ShaderKeyword.DefaultChainID;
            const kw = Shader._keyword;
            const mKW = materialKeywordID != null && materialKeywordID != defID ? ShaderKeyword.Get(materialKeywordID) : null;
            const oKW = otherKeywordID != null && otherKeywordID != defID ? ShaderKeyword.Get(otherKeywordID) : null;

            let variantID = kw.chainID == defID ? `` : `${kw.chainID}`;
            if (mKW) variantID += !variantID ? `${mKW.chainID}` : `_${mKW.chainID}`;
            if (oKW) variantID += !variantID ? `${oKW.chainID}` : `_${oKW.chainID}`;

            let variant = this._variantIDMap[variantID];

            //JIT 模式编译shader （即时用 ，即时编译）
            if (!variant) {
                let vsCode = this.vertexGLSL.getFull();
                let fsCode = this.fragmentGLSL.getFull();
                if (vsCode && fsCode) {
                    const keys = kw.getKeys();
                    const mKeys = mKW ? mKW.getKeys() : null;
                    const oKeys = oKW ? oKW.getKeys() : null;
                    let sc = variant = new ShaderCompiled(vsCode, fsCode, keys, mKeys, oKeys);
                    this._variantIDMap[variantID] = sc;
                } else {
                    console.warn(`fail code is null.`);
                    //给个 紫色的错误 着色器
                    variant = ShaderCompiled.errorFallBack;
                }
            }

            return variant;
        }

        /** 是否依赖资源全部加载完毕 */
        public isLoadedAll(): boolean {
            if (!this._isLoadAll && this.vertexGLSL && this.fragmentGLSL) {
                this._isLoadAll = true;
            }
            return this._isLoadAll;
        }

        /** 加载所有依赖的资源 */
        public async loadAllFile() {
            if (this._isLoadAll) return;
            if (!this.vertexFile) {
                console.error(`can't to load ,vertexShader is null.`);
                return;
            }
            if (!this.fragmentFile) {
                console.error(`can't to load ,fragmentShader is null.`);
                return;
            }

            let _idx = this.filePath.lastIndexOf("/");
            let folderPath = `${this.filePath.substring(0, _idx)}/`;

            let allFile: Promise<GLSL>[] = [];
            //vs load
            let fUrl = `${folderPath}${this.vertexFile}`;
            if (!AssetManager.GetAsset(fUrl)) { allFile.push(AssetManager.LoadFile(fUrl) as any); }
            //fs load
            fUrl = `${folderPath}${this.fragmentFile}`;
            if (!AssetManager.GetAsset(fUrl)) { allFile.push(AssetManager.LoadFile(fUrl) as any); }

            let glsls = await Promise.all(allFile)
                .catch((reason) => {
                    console.error(`glsl loaded fail. \n reason : ${reason}`);
                });
            //
            //load all
            if (glsls && glsls.length > 0) {
                for (let i = 0, len = glsls.length; i < len; i++) {
                    let _g = glsls[i];
                    if (_g.isLoadedAll()) continue;
                    await _g.loadAllFile();
                }
                //set shader
                this.vertexGLSL = glsls[0];
                this.fragmentGLSL = glsls[1];
            }

            this._isLoadAll = true;
        }

        /** 提交使用当前设置 */
        // tslint:disable-next-line: cyclomatic-complexity
        public apply() {
            let vsCode = this.vertexGLSL.getFull();
            let fsCode = this.fragmentGLSL.getFull();

            // console.log(`vsCode :${vsCode}`);
            // console.log(`fsCode :${fsCode}`);

            if (!fsCode || !vsCode) {
                console.error(`apply fail code is null.`);
                return;
            }

            // //变体处理
            // const variantKWs: string[][] = [];          //变体列表（关键字列表区分） , 默认无关键字
            // let mcs = this.multiCompiles.concat();
            // let hasMC = mcs && mcs.length > 0 && mcs[0].length > 0;
            // if (hasMC) {
            //     let mc = mcs.shift();
            //     while (mc) {
            //         //扩充
            //         if (mc.length < 1) continue;
            //         if (variantKWs.length == 0) {
            //             mc.forEach((v) => variantKWs.push([v]));
            //         } else {
            //             let oldLen = variantKWs.length;
            //             variantKWs.length *= mc.length;
            //             for (let i = 0, len = variantKWs.length; i < len; i++) {
            //                 const v = variantKWs[i];
            //                 if (v) continue;
            //                 variantKWs[i] = variantKWs[i % oldLen].concat();
            //             }
            //         }

            //         //填入 新的key
            //         for (let i = 0, len = variantKWs.length; i < len; i++) {
            //             const mcVal = mc[Math.floor(i / mc.length)];
            //             let arr = variantKWs[i];
            //             if (arr.indexOf(mcVal) != -1) continue;
            //             //加入到kw组合
            //             arr.push(mcVal);
            //         }

            //         mc = mcs.shift();
            //     }

            //     variantKWs.forEach((v) => {
            //         //关键字 排序
            //         v.sort();
            //         //去掉 nokey
            //         if (v.length > 1 && v[0] == "") {
            //             v.shift();
            //         }
            //     });
            // }

            // variantKWs.unshift([""]);

            // //编译所有变体着色器
            // for (let i = 0, len = variantKWs.length; i < len; i++) {
            //     const kws = variantKWs[i];
            //     kws.sort();
            //     const kwStr = kws.toString();
            //     //init Shaders
            //     const sc = new ShaderCompiled(vsCode, fsCode, kws);
            //     this._variantMap[kwStr] = sc;
            // }

            //definePropertyMap 映射为 _propertyMap
            const defPMap = this.definePropertyMap;
            if (defPMap) {
                const pKeys = Object.keys(defPMap);
                for (let i = 0, len = pKeys.length; i < len; i++) {
                    const pKey = pKeys[i];
                    const p = defPMap[pKey];
                    if (!p || p.type == null || p.value == null) continue;
                    switch (p.type) {
                        case "texture":
                            // tslint:disable-next-line: no-string-literal
                            let tEnum: DefTexture = (DefTexture as any)[(p.value as string)];
                            if (tEnum == null) tEnum = DefTexture.WHITE;
                            this._propertyMap[pKey] = DefaultAsset.GetTexture(tEnum);
                            break;
                        default:
                            const val = p.value;
                            this._propertyMap[pKey] = Array.isArray(val) ? new Float32Array(val) : new Float32Array([val as number]);
                    }
                }
            }
        }

        /**
         * 启用 全局关键字
         * @param keyword 关键字
         */
        public static enableKeyword(keyword: string) {
            if (!keyword) return;
            this._keyword = Shader._keyword.enable(keyword);
        }

        /**
         * 禁用 全局关键字
         * @param keyword 关键字
         */
        public static disableKeyword(keyword: string) {
            if (!keyword) return;
            this._keyword = Shader._keyword.disable(keyword);
        }

        /**
         * 全局关键字开关
         * @param keyword 关键字
         * @param enable 是否开启
         */
        public static switchKeyword(keyword: string, enable: boolean) {
            if (enable) {
                this.enableKeyword(keyword);
            } else {
                this.disableKeyword(keyword);
            }
        }

        /**
         * 获取 全局的Keyword ID
         * @returns shader的Keyword ID
         */
        public static getKeywordID() {
            return Shader._keyword.chainID;
        }

        // /** 刷新 关键字 字符串 */
        // private reflashKeywordStr() {
        //     this._currKeywords.sort();  //为了确保出来的key是一致的
        //     this._currKeywordStr = this._currKeywords.toString();
        // }

        /**
         * 设置 全局属性值 , 用于对应shader unifrom 全局字段
         * @param name 属性名字
         * @param value 值
         */
        public static setProperty(name: string, value: UniformUserValue | UniformUserValue[]) {
            ShaderUtil.SetProperty(this._globalPropertyMap, name, value);
        }
    }
}